package org.chen.aao.user.service.email;

import lombok.Data;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.chen.aao.common.exception.BusinessException;
import org.chen.aao.common.user.e.CodeType;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.Random;
import java.util.concurrent.TimeUnit;

@Slf4j
@Service
@RequiredArgsConstructor
public class VerificationCodeService {
    
    private final RedisTemplate<String, Object> redisTemplate;
    private final EmailService emailService; // 邮件服务
    
    // Redis key前缀
    private static final String CODE_KEY_PREFIX = "verification:code:";
    private static final String SEND_LIMIT_KEY_PREFIX = "verification:limit:";
    private static final String CODE_COUNT_KEY_PREFIX = "verification:count:";
    
    // 配置常量
    @Value("${app.verification.code.length}")
    private int CODE_LENGTH = 6;
    @Value("${app.verification.code.expire-minutes}")
    private int CODE_EXPIRE_MINUTES = 5;
    @Value("${app.verification.code.send-interval-seconds}")
    private int SEND_INTERVAL_SECONDS = 60;
    @Value("${app.verification.code.max-send-per-day}")
    private int MAX_SEND_COUNT_PER_DAY = 10;
    
    /**
     * 发送验证码
     */
    public void sendVerificationCode(String email, CodeType codeType) {
        // 1. 检查发送频率限制
        checkSendLimit(email, codeType);
        
        // 2. 检查每日发送次数限制
        checkDailyLimit(email, codeType);
        
        // 3. 生成验证码
        String code = generateCode();
        
        // 4. 保存验证码到Redis（新验证码会覆盖旧验证码）
        saveCode(email, code, codeType);
        
        // 5. 发送邮件
        sendEmail(email, code, codeType);
        
        // 6. 设置发送限制
        setSendLimit(email, codeType);
        
        // 7. 增加每日发送计数
        incrementDailyCount(email, codeType);
        
        log.info("验证码发送成功: email={}, type={}", email, codeType.getValue());
    }
    
    /**
     * 验证验证码
     */
    public boolean verifyCode(String email, String code, CodeType codeType) {
        String key = buildCodeKey(email, codeType);
        String storedCode = (String) redisTemplate.opsForValue().get(key);
        
        if (storedCode == null) {
            log.warn("验证码不存在或已过期: email={}, type={}", email, codeType.getValue());
            return false;
        }
        
        boolean isValid = storedCode.equals(code);
        
        if (isValid) {
            // 验证成功后删除验证码，防止重复使用
            redisTemplate.delete(key);
            log.info("验证码验证成功: email={}, type={}", email, codeType.getValue());
        } else {
            log.warn("验证码错误: email={}, type={}", email, codeType.getValue());
        }
        
        return isValid;
    }
    
    /**
     * 检查发送频率限制
     */
    private void checkSendLimit(String email, CodeType codeType) {
        String limitKey = buildSendLimitKey(email, codeType);
        Boolean hasLimit = redisTemplate.hasKey(limitKey);
        
        if (hasLimit) {
            Long ttl = redisTemplate.getExpire(limitKey, TimeUnit.SECONDS);
            throw new BusinessException(String.format("请求过于频繁，请%d秒后再试", ttl));
        }
    }
    
    /**
     * 检查每日发送次数限制
     */
    private void checkDailyLimit(String email, CodeType codeType) {
        String countKey = buildCountKey(email, codeType);
        Integer count = (Integer) redisTemplate.opsForValue().get(countKey);
        
        if (count != null && count >= MAX_SEND_COUNT_PER_DAY) {
            throw new BusinessException("今日验证码发送次数已达上限，请明天再试");
        }
    }
    
    /**
     * 生成验证码
     */
    private String generateCode() {
        Random random = new Random();
        StringBuilder code = new StringBuilder();
        for (int i = 0; i < CODE_LENGTH; i++) {
            code.append(random.nextInt(10));
        }
        return code.toString();
    }
    
    /**
     * 保存验证码到Redis
     */
    private void saveCode(String email, String code, CodeType codeType) {
        String key = buildCodeKey(email, codeType);
        
        // 保存新验证码，会自动覆盖旧的
        redisTemplate.opsForValue().set(key, code, CODE_EXPIRE_MINUTES, TimeUnit.MINUTES);
        
        // 同时保存验证码的元数据（可选，用于日志或审计）
        String metaKey = key + ":meta";
        VerificationCodeMeta meta = new VerificationCodeMeta();
        meta.setEmail(email);
        meta.setCodeType(codeType.getValue());
        meta.setCreateTime(System.currentTimeMillis());
        meta.setExpireTime(System.currentTimeMillis() + (long) CODE_EXPIRE_MINUTES * 300 * 1000);
        
        redisTemplate.opsForValue().set(metaKey, meta, CODE_EXPIRE_MINUTES, TimeUnit.MINUTES);
    }
    
    /**
     * 发送邮件
     */
    private void sendEmail(String email, String code, CodeType codeType) {
        String subject = "【AI工具箱】" + codeType.getDescription() + "验证码";
        String content = buildEmailContent(code, codeType);
        
        try {
            emailService.sendHtmlEmail(email, subject, content);
        } catch (Exception e) {
            log.error("邮件发送失败: email={}, error={}", email, e.getMessage());
            // 发送失败时清理已保存的验证码
            String key = buildCodeKey(email, codeType);
            redisTemplate.delete(key);
            throw new BusinessException("邮件发送失败，请稍后重试");
        }
    }
    
    /**
     * 构建邮件内容
     */
    private String buildEmailContent(String code, CodeType codeType) {
        return String.format(
            "<html>" +
            "<body style='font-family: Arial, sans-serif;'>" +
            "<div style='max-width: 600px; margin: 0 auto; padding: 20px;'>" +
            "<h2 style='color: #667eea;'>AI工具箱 - %s</h2>" +
            "<p>您的验证码是：</p>" +
            "<div style='background: #f5f5f5; padding: 15px; border-radius: 5px; margin: 20px 0;'>" +
            "<h1 style='color: #333; text-align: center; letter-spacing: 5px; margin: 0;'>%s</h1>" +
            "</div>" +
            "<p style='color: #666;'>验证码有效期为%d分钟，请尽快使用。</p>" +
            "<p style='color: #999; font-size: 12px;'>如果这不是您的操作，请忽略此邮件。</p>" +
            "</div>" +
            "</body>" +
            "</html>",
            codeType.getDescription(),
            code,
            CODE_EXPIRE_MINUTES
        );
    }
    
    /**
     * 设置发送频率限制
     */
    private void setSendLimit(String email, CodeType codeType) {
        String limitKey = buildSendLimitKey(email, codeType);
        redisTemplate.opsForValue().set(limitKey, "1", SEND_INTERVAL_SECONDS, TimeUnit.SECONDS);
    }
    
    /**
     * 增加每日发送计数
     */
    private void incrementDailyCount(String email, CodeType codeType) {
        String countKey = buildCountKey(email, codeType);
        Long count = redisTemplate.opsForValue().increment(countKey);
        
        if (count != null && count == 1) {
            // 第一次发送，设置过期时间为当天结束
            redisTemplate.expire(countKey, getSecondsUntilEndOfDay(), TimeUnit.SECONDS);
        }
    }
    
    /**
     * 获取到当天结束的秒数
     */
    private long getSecondsUntilEndOfDay() {
        long currentTime = System.currentTimeMillis();
        long endOfDay = (currentTime / (24 * 60 * 60 * 1000) + 1) * (24 * 60 * 60 * 1000);
        return (endOfDay - currentTime) / 1000;
    }
    
    /**
     * 构建验证码存储key
     */
    private String buildCodeKey(String email, CodeType codeType) {
        return CODE_KEY_PREFIX + codeType.getValue() + ":" + email;
    }
    
    /**
     * 构建发送限制key
     */
    private String buildSendLimitKey(String email, CodeType codeType) {
        return SEND_LIMIT_KEY_PREFIX + codeType.getValue() + ":" + email;
    }
    
    /**
     * 构建每日计数key
     */
    private String buildCountKey(String email, CodeType codeType) {
        return CODE_COUNT_KEY_PREFIX + codeType.getValue() + ":" + email;
    }
    
    /**
     * 验证码元数据（用于审计）
     */
    @Data
    private static class VerificationCodeMeta {
        private String email;
        private String codeType;
        private Long createTime;
        private Long expireTime;
    }
}